import { BufferGeometry, BufferAttribute, EdgesGeometry, Vector3 } from 'three'
import { Delaunay } from 'd3-delaunay'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'
// import { LoopSubdivision } from 'three-subdivide'

export class StratumGeometry extends BufferGeometry {
  constructor(layerData) {
    super()

    this.type = 'StratumGeometry'

    this.parameters = {
      layerData: layerData
    }

    // create buffer data
    return generateBufferData()
    // functions

    function generateBufferData() {
      const { plane, plane1 } = createdPlanes()
      const vecs1 = getEdgeVertices(plane)
      const vecs2 = getEdgeVertices(plane1)
      const connected = createConnectedGeometry(vecs1, vecs2)
      const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries([plane, plane1, connected])
      return mergedGeometry
    }

    function createdPlanes() {
      // 2. Delaunay 三角剖分（转换为二维坐标）
      const delaunayPoints = layerData.map((p) => [p[0], p[1]])
      const delaunay = Delaunay.from(delaunayPoints)
      const triangles = delaunay.triangles // 获得三角形索引数组
      // // 3. 创建几何体并设置顶点
      const plane = new BufferGeometry()
      const plane1 = new BufferGeometry()
      const positions = new Float32Array(layerData.length * 3)
      const positions1 = new Float32Array(layerData.length * 3)
      let minX = Infinity,
        maxX = -Infinity,
        minY = Infinity,
        maxY = -Infinity
      layerData.forEach((p, i) => {
        positions[i * 3] = p[0]
        positions[i * 3 + 1] = p[1]
        positions[i * 3 + 2] = p[2]

        positions1[i * 3] = p[0]
        positions1[i * 3 + 1] = p[1]
        positions1[i * 3 + 2] = p[3]

        minX = Math.min(minX, p[0])
        maxX = Math.max(maxX, p[0])
        minY = Math.min(minY, p[1])
        maxY = Math.max(maxY, p[1])
      })
      const uvs = new Float32Array(layerData.length * 2)
      layerData.forEach((p, i) => {
        const u = (p[0] - minX) / (maxX - minX)
        const v = (p[1] - minY) / (maxY - minY)
        uvs[i * 2] = u
        uvs[i * 2 + 1] = v
      })
      // positions
      plane.setAttribute('position', new BufferAttribute(positions, 3))
      plane1.setAttribute('position', new BufferAttribute(positions1, 3))
      // uv
      plane.setAttribute('uv', new BufferAttribute(uvs, 2))
      plane1.setAttribute('uv', new BufferAttribute(uvs, 2))
      // 4. 设置三角形索引
      plane.setIndex([...triangles])
      plane1.setIndex([...triangles])
      // 5. 计算法线（用于光照）
      plane.computeVertexNormals()
      plane1.computeVertexNormals()
      // const geometry1 = LoopSubdivision.modify(geometry, 1, {
      //   preserveEdges: true,
      //   maxTriangles: 1000
      // })
      console.log(plane)
      console.log(plane1)
      return { plane, plane1 }
    }

    function createConnectedGeometry(vertices1, vertices2) {
      const allVertices = [...vertices1, ...vertices2]
      let minX = Infinity,
        maxX = -Infinity,
        minY = Infinity,
        maxY = -Infinity
      const positions = new Float32Array(allVertices.length * 3)
      allVertices.forEach((v, i) => {
        positions[i * 3] = v.x
        positions[i * 3 + 1] = v.y
        positions[i * 3 + 2] = v.z
        minX = Math.min(minX, v.x)
        maxX = Math.max(maxX, v.x)
        minY = Math.min(minY, v.z)
        maxY = Math.max(maxY, v.z)
      })

      const geometry = new BufferGeometry()
      geometry.setAttribute('position', new BufferAttribute(positions, 3))
      // 生成侧面三角形索引
      const indices = []
      const offset = vertices1.length
      for (let i = 0; i < vertices1.length; i++) {
        const next = (i + 1) % vertices1.length
        indices.push(i, next, offset + next)
        indices.push(i, offset + next, offset + i)
      }
      const uvs = new Float32Array(allVertices.length * 2)
      allVertices.forEach((p, i) => {
        const u = (p.x - minX) / (maxX - minX)
        const v = (p.z - minY) / (maxY - minY)
        uvs[i * 2] = u
        uvs[i * 2 + 1] = v
      })
      geometry.setIndex(indices)
      geometry.computeVertexNormals()
      geometry.setAttribute('uv', new BufferAttribute(uvs, 2))
      return geometry
    }

    function getEdgeVertices(geometry) {
      const edges = new EdgesGeometry(geometry, 150)
      const position = edges.attributes.position.array
      const edgeVertices = []
      for (let index = 0; index < position.length; index += 3) {
        const vec3 = new Vector3(position[index], position[index + 1], position[index + 2])
        if (!edgeVertices.find((vec) => vec.equals(vec3))) {
          edgeVertices.push(new Vector3(position[index], position[index + 1], position[index + 2]))
        }
      }
      const pos = orderPointsWithGrid(edgeVertices)
      return pos
    }
  }

  toJSON() {
    const data = super.toJSON()

    data.path = this.parameters.path.toJSON()

    return data
  }

  static fromJSON(data) {
    const vecs = data.path.points.map((item) => {
      return new Vector3(...item)
    })
    const path = new CatmullRomCurve3(vecs)
    const geo = new TunnelGeometry(path, TunnelGeometry.pathToUMapping(path, 0, 0.1))
    geo.center()
    return geo
  }

  static pathToUMapping(
    path = new CatmullRomCurve3([
      new Vector3(-1, -1, 0),
      new Vector3(-1, 1, 0),
      new Vector3(1, 1, 0)
    ]),
    elbowSegmentNum = 2,
    elbowSegmentOffset = 0.1
  ) {
    const lengths = [0]
    path.points.forEach((p, i, arr) => {
      if (i > 0) {
        const last = lengths.at(-1) // 获取到最后一个
        const dist = p.distanceTo(arr[i - 1]) // 计算当前点和上一个点的距离
        const next = last + dist // 计算下一个
        const numElbow = Math.min(elbowSegmentNum, dist / 2 / elbowSegmentOffset - 1) // 计算需要多少个elbow
        if (i > 1) {
          for (let j = 1; j <= numElbow; ++j) {
            lengths.push(last + j * elbowSegmentOffset) // 计算每个elbow
          }
        }
        if (i < arr.length - 1) {
          for (let j = numElbow; j >= 1; --j) {
            lengths.push(next - j * elbowSegmentOffset)
          }
        }
        lengths.push(next)
      }
    })

    const uMappingFrames = lengths.map((l) => l / lengths.at(-1))

    return uMappingFrames
  }
}

class SpatialGrid {
  constructor(points, gridSize = 1.0) {
    this.grid = new Map()
    this.gridSize = gridSize

    // 将点分配到网格
    points.forEach((point, index) => {
      const key = this._getGridKey(point)
      if (!this.grid.has(key)) {
        this.grid.set(key, [])
      }
      this.grid.get(key).push({ point, index })
    })
  }

  // 获取点所在网格的键（二维示例）
  _getGridKey(point) {
    const x = Math.floor(point.x / this.gridSize)
    const y = Math.floor(point.y / this.gridSize)
    return `${x},${y}`
  }

  // 查找邻近网格中的点
  findNeighbors(point, radius = 2) {
    const neighbors = []
    const baseKey = this._getGridKey(point)
    const [baseX, baseY] = baseKey.split(',').map(Number)

    // 检查周围3x3网格
    for (let dx = -radius; dx <= radius; dx++) {
      for (let dy = -radius; dy <= radius; dy++) {
        const key = `${baseX + dx},${baseY + dy}`
        if (this.grid.has(key)) {
          neighbors.push(...this.grid.get(key))
        }
      }
    }
    return neighbors
  }
}

// ========== 使用示例 ==========
function orderPointsWithGrid(points, gridSize = 1.0) {
  const grid = new SpatialGrid(points, gridSize)
  const ordered = []
  const remaining = new Set(points.map((_, i) => i))

  // 自动检测端点（度数=1的点）
  const endpoints = []
  points.forEach((point, index) => {
    const neighbors = grid
      .findNeighbors(point)
      .filter((n) => n.index !== index)
      .map((n) => n.point)

    // 计算邻近点数量（距离小于阈值）
    const closeNeighbors = neighbors.filter((p) => p.distanceTo(point) < gridSize * 1.5)
    if (closeNeighbors.length <= 1) {
      endpoints.push(index)
    }
  })

  // 选择端点作为起点
  let currentIndex = endpoints[0]
  ordered.push(points[currentIndex])
  remaining.delete(currentIndex)

  // 贪心算法排序
  while (remaining.size > 0) {
    const currentPoint = points[currentIndex]
    let closestDist = Infinity
    let closestIndex = -1

    // 仅在邻近网格中搜索
    grid.findNeighbors(currentPoint).forEach(({ point, index }) => {
      if (!remaining.has(index)) return
      const dist = currentPoint.distanceTo(point)
      if (dist < closestDist) {
        closestDist = dist
        closestIndex = index
      }
    })

    // 如果邻近网格未找到，全局搜索
    if (closestIndex === -1) {
      remaining.forEach((index) => {
        const dist = currentPoint.distanceTo(points[index])
        if (dist < closestDist) {
          closestDist = dist
          closestIndex = index
        }
      })
    }

    ordered.push(points[closestIndex])
    remaining.delete(closestIndex)
    currentIndex = closestIndex
  }

  return ordered
}
